import type { AppContext } from '../types/index.js';
import { logger } from '../utils/logger.js';
import { config } from '../config/env.js';
import { BaseException } from '../types/exceptions.js';
import { Response } from '../utils/response.js';

/**
 * 格式化请求日志
 * @param ctx 请求上下文
 * @returns 请求日志
 */
function formatRequest(ctx: AppContext): string {
  const log_text: string[] = [];
  log_text.push(`request url: ${ctx.method} ${ctx.originalUrl}`);
  log_text.push(`request ip: ${ctx.ip}`);

  if (ctx.method === 'GET') {
    log_text.push(`request query: ${JSON.stringify(ctx.request.query)}`);
  } else {
    log_text.push(`request body: ${JSON.stringify(ctx.request.body)}`);
  }
  return log_text.join('\n');
}

/**
 * 格式化响应日志
 * @param ctx 请求上下文
 * @param duration 请求耗时
 * @returns 日志
 */
function formatResponse(ctx: AppContext, duration: number): string {
  const log_text: string[] = [];
  log_text.push('*************** response log start ***************');
  log_text.push(...formatRequest(ctx));
  log_text.push(`response time: ${duration}ms`);
  log_text.push(`response status: ${ctx.status}`);
  log_text.push(`response body: ${JSON.stringify(ctx.body)}`);
  log_text.push('*************** response log end ***************');
  return log_text.join('\n');
}

/**
 * 格式化错误日志
 * @param ctx 请求上下文
 * @param error 异常信息
 * @param duration 请求耗时
 * @returns 日志
 */
function formatError(ctx: AppContext, error: Error, duration: number): string {
  const log_text: string[] = [''];
  log_text.push('*************** error log start ***************');
  log_text.push(...formatRequest(ctx));
  log_text.push(`response time: ${duration}ms`);

  if (error instanceof BaseException) {
    log_text.push(`error code: ${error.code}`);
  }
  log_text.push(`error stack: ${error.stack}`);
  log_text.push('*************** error log end ***************');

  return log_text.join('\n');
}

function insertLoggerToDB() {
  // TODO: 插入日志到数据库
  console.log('==== TODO : 插入日志到数据库 ====');
}

export async function loggerMiddleware(ctx: AppContext, next: () => Promise<void>): Promise<void> {
  const start = Date.now();
  try {
    await next();
  } catch (error) {
    const ms = Date.now() - start;
    logger.error(formatError(ctx, error as Error, ms));
    if (error instanceof BaseException) {
      Response.error(error.code, error.message);
    } else {
      Response.serverError('服务器内部错误');
    }
  } finally {
    const ms = Date.now() - start;
    // 如果配置错误日志入库
    if (config.app.saveLogs && ctx.method !== 'GET') {
      insertLoggerToDB();
    }
    logger.access(formatResponse(ctx, ms));
  }
}
